package org.kernely.scheduling.administration;

import java.text.ParseException;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;

import org.kernely.scheduling.KernelyQuartzScheduler;
import org.kernely.scheduling.SchedulingBundle;
import org.kernely.ui.mvp.KernelyEventSender;
import org.kernely.ui.mvp.KernelyPresenter;
import org.kernely.ui.utils.KernelyNotification;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.SchedulerException;

import com.vaadin.data.Property;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.terminal.UserError;
import com.vaadin.ui.Button;
import com.vaadin.ui.Window;
import com.vaadin.ui.Button.ClickListener;

/**
 * Presenter which permits to add or edit a job.
 */
public class AddEditJobPresenter extends KernelyPresenter<AddEditJobView> implements KernelyEventSender{

	/**
	 * The JobDetail and the Trigger to edit in edition mode
	 */
	private SchedulingAdminPresenter.JobAndTriggerIdentifier identifier;
	private boolean editMode = false;

	/**
	 * Default constructor in creation mode
	 */
	public AddEditJobPresenter(){}

	/**
	 * Constructor in edition mode
	 * @param ud : the UserDetails to edit
	 */
	public AddEditJobPresenter(SchedulingAdminPresenter.JobAndTriggerIdentifier identifier){
		this.identifier = identifier;
		this.editMode = true;
	}

	/**
	 * @see org.kernely.ui.mvp.KernelyPresenter#bind()
	 */
	@SuppressWarnings("serial")
	@Override
	public final void bind() {

		if (editMode){
			this.view.getValidateButton().setCaption("Edit");
		} else {
			this.view.getValidateButton().setCaption("Schedule");			
		}
		
		this.view.getJobsBox().addListener(new Property.ValueChangeListener(){

			/**
			 * @see com.vaadin.data.Property.ValueChangeListener#valueChange(com.vaadin.data.Property.ValueChangeEvent)
			 */
			public void valueChange(ValueChangeEvent event) {
				setDescription();
			}
			
		});
		
		// Fill the jobs combo box
		Map<Class <? extends Job>, JobDetail> map = SchedulingBundle.getSchedulingService().getRegisteredJobs();
		for (Entry<Class<? extends Job>, JobDetail> entry : map.entrySet()){
			this.view.getJobsBox().addItem(entry.getKey());
			this.view.getJobsBox().setItemCaption(entry.getKey(),entry.getKey().getName());
		}
		
		if (editMode){
			// Select the class of the edited JobDetail
			this.view.getJobsBox().setValue(this.identifier.getJob().getJobClass());
		} else {
			// Select the first item
			this.view.getJobsBox().setValue(this.view.getJobsBox().getItemIds().iterator().next());
		}
		if (editMode){
			this.view.getCronExpressionField().setValue(this.identifier.getTrigger().getCronExpression());
			this.view.getStartDateField().setValue(this.identifier.getTrigger().getStartTime());
			this.view.getEndDateField().setValue(this.identifier.getTrigger().getEndTime());
		}
		
		this.view.getValidateButton().addListener(new ClickListener(){
			/**
			 * @see com.vaadin.ui.Button.ClickListener#buttonClick(com.vaadin.ui.Button.ClickEvent)
			 */
			public void buttonClick (Button.ClickEvent event) {
				validate();
			}
		});

	}

	@SuppressWarnings("unchecked")
	private void validate(){
		// Get the job class and details
		Class<? extends Job> jobClass = (Class<? extends Job>) this.view.getJobsBox().getValue();
		Map<Class <? extends Job>, JobDetail> map = SchedulingBundle.getSchedulingService().getRegisteredJobs();

		// If edition mode, unschedule the previous job
		if (editMode){
			try {
				KernelyQuartzScheduler.getScheduler().unscheduleJob(this.identifier.getTrigger().getName(),this.identifier.getJob().getGroup());
			} catch (SchedulerException e) {
				this.view.getWindow().showNotification(new KernelyNotification("The job have not been unscheduled. Cause: "+e.getMessage(),KernelyNotification.ERROR_ICON));
			}
		}
		
		// Schedule the new job
		try {
			String cronExpression = this.view.getCronExpressionField().toString();
			SchedulingBundle.getSchedulingService().scheduleRegisteredJob(
					jobClass,
					cronExpression,
					(Date) view.getStartDateField().getValue(),
					(Date) view.getEndDateField().getValue());
					
			// Send an UI event to refresh UI
			SchedulingRefreshEvent event = new SchedulingRefreshEvent(this);
			SchedulingBundle.getEventBus().broadcastEvent(event);

			// Show a notification
			view.getWindow().showNotification(new KernelyNotification("Job "+jobClass+" successfully scheduled with cron expression "+cronExpression, KernelyNotification.INFO_ICON));
			
			// Close the window when operation succeeded
			Window win = (Window)view.getParent();
			win.getParent().removeWindow(win);

		} catch (ParseException e) {
			view.getWindow().showNotification(new KernelyNotification(e.getMessage(), KernelyNotification.ERROR_ICON));
			this.view.getCronExpressionField().setComponentError(new UserError(e.getMessage()));
		}
	}

	// Set the description label with the selected job description
	@SuppressWarnings("unchecked")
	private void setDescription(){
		Class <? extends Job> selected = ((Class <? extends Job>) this.view.getJobsBox().getValue());
		Map<Class <? extends Job>, JobDetail> map = SchedulingBundle.getSchedulingService().getRegisteredJobs();
		String description = map.get(selected).getDescription();
		
		this.view.getDescriptionLabel().setValue(description);
	}

}
